package com.guan.friend.service.user.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.guan.common.core.constants.CacheConstants;
import com.guan.common.core.constants.Constants;
import com.guan.common.core.constants.HttpConstants;
import com.guan.common.core.domain.LoginUser;
import com.guan.common.core.domain.VO.LoginUserVO;
import com.guan.common.core.enums.ResultCode;
import com.guan.common.core.enums.UserIdentity;
import com.guan.common.core.utils.ThreadLocalUtil;
import com.guan.common.message.service.AliSmsService;
import com.guan.friend.domain.user.User;
import com.guan.friend.domain.user.dto.UserDTO;
import com.guan.friend.domain.user.dto.UserDetailUpdateDTO;
import com.guan.friend.domain.user.vo.UserVO;
import com.guan.friend.manager.UserCacheManager;
import com.guan.friend.mapper.user.UserMapper;
import com.guan.friend.service.user.IUserService;
import com.guan.ojcommonredis.service.RedisService;
import com.guan.ojcommonsecurity.exception.ServiceException;
import com.guan.ojcommonsecurity.service.TokenService;
import io.netty.util.internal.StringUtil;
import jakarta.annotation.Resource;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    UserMapper userMapper;

    @Resource
    private AliSmsService aliSmsService;

    @Resource
    TokenService tokenService;

    @Resource
    private RedisService redisService;

    @Resource
    private UserCacheManager userCacheManager;

    @Value("${sms.codeExpiration}")
    private Long codeExpiration;

    @Value("${sms.codeCountLimit}")
    private Long codeCountLimit;

    @Value("${sms.isSend:false}")
    private boolean isSend; // true:生成随机验证码并且发送  false：生成固定验证码 并且 不发送

    @Value("${jwt.secret}")
    private String secret;

    @Value("${aliyun.oss.downloadUrl}")
    private String downloadUrl;


    @Override
    public Boolean sendCode(UserDTO userDTO) {
        if (!isValidPhoneNumber(userDTO.getPhone())) {
            throw new ServiceException(ResultCode.FAILED_USER_PHONE);
        }
        // 检测是否能够重新获取验证码
        String key = getPhoneCodeKey(userDTO.getPhone());
        Long expire = redisService.getExpire(key, TimeUnit.SECONDS);
        if (expire != null && codeExpiration * 60 - expire < 60) {
            throw new ServiceException(ResultCode.FAILED_FREQUENT);
        }
        // 每天 验证码最多获取次数设置上限，方式恶意用户攻击 redis
        // key:"c:c:手机号"
        String codeTimeKey = getCodeTimeKey(userDTO);
        Long sendCount = redisService.getCacheObject(codeTimeKey, Long.class);
        if (sendCount != null && codeCountLimit <= sendCount) {
            throw new ServiceException(ResultCode.FAILED_COUNT_LIMIT);
        }

        // 根据isSend判断 生成随机验证码还是测试验证码
        String code = isSend ? RandomUtil.randomNumbers(6) : Constants.DEFAULT_CODE;
        // 存储到redis String  key："p:c:手机号", value: "code" 过期时间通过 nacos 获取
        redisService.setCacheObject(key, code, codeExpiration, TimeUnit.MINUTES);
        if(isSend){// isSend == true 发送验证码
            boolean res = aliSmsService.sendMobileCode(userDTO.getPhone(), code);
            if (!res) {
                throw new ServiceException(ResultCode.FAILED_SEND_CODE);
            }
        }
        // codeTimeKey++
        redisService.increment(codeTimeKey);

        // 更新每日获取次数，设置 codeTimeKey 的过期时间仅在第一次或过期后
        Long ttl = getRemainingSecondsOfToday(); // 计算当天剩余时间（秒）
        Long currentTtl = redisService.getExpire(codeTimeKey, TimeUnit.SECONDS);
        if (currentTtl == null || currentTtl <= 0) {
            redisService.expire(codeTimeKey, ttl, TimeUnit.SECONDS);
        }
        return true;
    }

    @Override
    public String codeLogin(String phone, String code) {
        // 获取redis中的验证码并和用户输入的进行校验
        checkCode(phone, code);

        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));
        if (user == null) {// 新用户
            //注册逻辑
            user = new User();
            user.setPhone(phone);
            user.setCreateBy(Constants.SYS_USER_ADMIN);
            userMapper.insert(user);
        }
        // 已经注册过 进行登录
        // 生成Token
        String token = tokenService.creatToken(user.getUserId(), user.getNickName(), user.getHeadImage(), secret, UserIdentity.ORDINARY.getValue());
        //System.out.println("token: "+ token);
        return token;

    }

    @Override
    public boolean logout(String token) {
        // 1. 校验 token 的身份合法性
        LoginUser loginUser = tokenService.parseToken2LoginUser(token,secret);
        if (loginUser == null || loginUser.getIdentity() != UserIdentity.ORDINARY.getValue()) {
            throw new ServiceException(ResultCode.TOKEN_PARAMS_VALIDATE);// Token 无效
        }

        // 2. 移除用户的登录状态（清除 Redis 中的 token 信息）
        boolean isRemoved = tokenService.removeToken(token,secret);
        if (!isRemoved) {
            throw new ServiceException(ResultCode.FAILED_LOGOUT); // 登出失败
        }

        // 3. 返回成功响应
        return true;
    }

    @Override
    public LoginUserVO info(String token) {
        // 如果前端设置了令牌前缀，则裁剪掉前缀
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        // 从redis获取 loginUser
        LoginUser loginUser = tokenService.parseToken2LoginUser(token,secret);
        if(loginUser == null) return null;
        // loginUser --> loginUserVO
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        if(StrUtil.isNotEmpty(loginUser.getHeadImage())){
            loginUserVO.setHeadImage(downloadUrl + loginUser.getHeadImage());
        }
        return loginUserVO;
    }

    @Override
    public UserVO detail() {
        Long userID = ThreadLocalUtil.get(Constants.TTL_KEY_USER_ID, Long.class);
        if(userID == null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        UserVO userVO = userCacheManager.getUserById(userID);
        if (userVO == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        if(StrUtil.isNotEmpty(userVO.getHeadImage())){
            userVO.setHeadImage(downloadUrl + userVO.getHeadImage());
        }
        return userVO;
    }

    @Override
    public int edit(UserDetailUpdateDTO userDetailDTO) {
        Long userId = ThreadLocalUtil.get(Constants.TTL_KEY_USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setNickName(userDetailDTO.getNickName());
        user.setSex(userDetailDTO.getSex());
        user.setSchoolName(userDetailDTO.getSchoolName());
        user.setMajorName(userDetailDTO.getMajorName());
        user.setPhone(userDetailDTO.getPhone());
        user.setEmail(userDetailDTO.getEmail());
        user.setWechat(userDetailDTO.getWechat());
        user.setIntroduce(userDetailDTO.getIntroduce());
        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.TTL_KEY_USER_KEY, String.class));
        return userMapper.updateById(user);
    }

    @Override
    public int updateHeadImage(UserDetailUpdateDTO userDetailUpdateDTO) {
        Long userId = ThreadLocalUtil.get(Constants.TTL_KEY_USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setHeadImage(userDetailUpdateDTO.getHeadImage());
        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.TTL_KEY_USER_KEY, String.class));
        return userMapper.updateById(user);

    }

    private void checkCode(String phone, String code) {
        String phoneCodeKey = getPhoneCodeKey(phone);
        String cacheCode = redisService.getCacheObject(phoneCodeKey, String.class);
        if (cacheCode == null) {
            throw new ServiceException(ResultCode.FAILED_CODE_INVALID);
        }
        if (!cacheCode.equals(code)) {
            throw new ServiceException(ResultCode.FAILED_CODE_ERROR);
        }
        // 验证码释放
        redisService.deleteObject(phoneCodeKey);
    }

    /**
     * 获取当天剩余的秒数
     *
     * @return 剩余秒数
     */
    private Long getRemainingSecondsOfToday() {
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        // 获取当天的结束时间
        LocalDateTime endOfDay = now.toLocalDate().atTime(23, 59, 59);
        // 计算剩余秒数
        return Duration.between(now, endOfDay).getSeconds();
    }

    private String getCodeTimeKey(UserDTO userDTO) {
        return CacheConstants.CODE_COUNT_KEY + userDTO.getPhone();
    }

    @NotNull
    private String getPhoneCodeKey(String phone) {
        String key = CacheConstants.PHONE_CODE_KEY + phone;
        return key;
    }

    private boolean isValidPhoneNumber(String phone) {
        // 定义手机号的正则表达式
        String phoneRegex = "^1[3-9]\\d{9}$";

        // 使用正则表达式进行匹配
        return phone != null && phone.matches(phoneRegex);
    }

}
